﻿using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace SPWebPartReplace
{
    /// <summary>
    /// Allows inherited classes to perform SharePoint operations using
    /// an elevated SPSite and SPWeb member instead of using a delegate.
    /// </summary>
    public class SharePointOperationBase : IDisposable
    {
        /// <summary>
        /// Elevated SPWeb
        /// </summary>
        protected readonly SPWeb Web;

        /// <summary>
        /// Elevated SPSite
        /// </summary>
        protected readonly SPSite Site;

        /// <summary>
        /// Elevated SPWebApplication
        /// </summary>
        protected readonly SPWebApplication WebApplication;

        /// <summary>
        /// Elevated SPFarm
        /// </summary>
        protected readonly SPFarm Farm;

        private readonly string _webUrl;

        /// <summary>
        /// Initializes a new instance of the <see cref="SharePointOperationBase"/> class.
        /// Creates an elevated SPSite and SPWeb member.
        /// </summary>
        public SharePointOperationBase()
        {
            Site = SPContext.Current.Site;
            SPUserToken systemUserToken = GetSystemToken(Site);

            _webUrl = Site.Url;

            Site = new SPSite(_webUrl, systemUserToken);
            Site.AllowUnsafeUpdates = true;
            Web = Site.OpenWeb();
            Web.AllowUnsafeUpdates = true;

            WebApplication = Site.WebApplication;

            Farm = WebApplication.Farm;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SharePointOperationBase"/> class.
        /// Creates an elevated SPSite and SPWeb member.
        /// </summary>
        /// <param name="webUrl">The site URL.</param>
        public SharePointOperationBase(string webUrl)
        {
            _webUrl = webUrl;
            SPUserToken systemUserToken;
            using (Site = new SPSite(_webUrl))
            {
                systemUserToken = GetSystemToken(Site);
            }

            Site = new SPSite(_webUrl, systemUserToken);
            Site.AllowUnsafeUpdates = true;
            Web = Site.OpenWeb();
            Web.AllowUnsafeUpdates = true;

            WebApplication = Site.WebApplication;

            Farm = WebApplication.Farm;
        }

        /// <summary>
        /// Gets the system security token.
        /// </summary>
        /// <param name="site">The site.</param>
        /// <returns></returns>
        public static SPUserToken GetSystemToken(SPSite site)
        {
            SPUserToken token = null;
            bool accessDenied = site.CatchAccessDeniedException;
            try
            {
                site.CatchAccessDeniedException = false;
                token = site.SystemAccount.UserToken;
            }
            catch (UnauthorizedAccessException)
            {
                SPSecurity.RunWithElevatedPrivileges(() =>
                {
                    using (SPSite elevSite = new SPSite(site.ID))
                        token = elevSite.SystemAccount.UserToken;
                });
            }
            finally
            {
                site.CatchAccessDeniedException = accessDenied;
            }
            return token;
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing,
        /// or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if (Site != null)
            {
                Web.AllowUnsafeUpdates = false;
                Web.Dispose();
                Site.AllowUnsafeUpdates = false;
                Site.Dispose();
            }
        }
    }
}
